1 module hip.util.format; 2 import hip.util.string:count; 3 import hip.util.conv:to; 4 5 string format(string fmt, Args...)(Args a) 6 { 7 string ret = ""; 8 int currArg = 0; 9 10 string[] converted; 11 static foreach(arg; a) 12 converted~= to!string(arg); 13 14 static assert(fmt.count("%s") == a.length, "Format specifiers count must match arguments count"); 15 16 for(int i = 0; i < fmt.length; i++) 17 { 18 if(i + 1 < fmt.length && fmt[i] == '%' && fmt[i+1] == 's') 19 { 20 i++; 21 ret~= converted[currArg++]; 22 } 23 else 24 ret~= fmt[i]; 25 } 26 return ret; 27 } 28 29 string formatFromType(T)(T value) 30 { 31 string ret; 32 mixin(() 33 { 34 string base = T.init.toString; 35 ptrdiff_t start = -1, lastCapture; 36 string ret; 37 foreach(i, c; base) 38 { 39 import hip.util.string; 40 if(start != -1 && (isWhitespace(c) || !(isAlpha(c) || isNumeric(c)))) 41 { 42 ret~= "ret~= \""~base[lastCapture..start]~"\";"; 43 ret~= "ret~= value."~base[start+1..i]~".to!string;"; 44 lastCapture = i; 45 start = -1; 46 } 47 else if(c == '$') 48 start = i; 49 } 50 ret~= "ret~= \""~base[lastCapture..$]~"\";"; 51 return ret; 52 }()); 53 return ret; 54 } 55 56 void formatFromType(T, Sink)(ref Sink s, T value) 57 { 58 import hip.util.to_string_range; 59 mixin(() 60 { 61 string base = T.init.toString; 62 ptrdiff_t start = -1, lastCapture; 63 string ret; 64 foreach(i, c; base) 65 { 66 import hip.util.string; 67 if(start != -1 && (isWhitespace(c) || !(isAlpha(c) || isNumeric(c)))) 68 { 69 ret~= "put(s, \""~base[lastCapture..start]~"\");"; 70 ret~= "toStringRange(s, value."~base[start+1..i]~");"; 71 lastCapture = i; 72 start = -1; 73 } 74 else if(c == '$') 75 start = i; 76 } 77 ret~= "put(s, \""~base[lastCapture..$]~"\");"; 78 return ret; 79 }()); 80 } 81 82 /** 83 * Unsafe function. It requires the programmer to use it correctly as the buffer size is preallocated. 84 */ 85 string fastUnsafeCTFEFormat(in string str, string[] replaceWith...) @trusted 86 { 87 assert(__ctfe, "Can't be called on runtime. To force a CTFE usage, call as `enum variable = \"%s a = 500;\".fastUnsafeCTFEFormat(\"int\");`"); 88 char[] buffer; 89 ptrdiff_t replaceSize; 90 foreach(r; replaceWith) 91 replaceSize+= cast(ptrdiff_t)r.length - cast(ptrdiff_t)"%s".length; 92 buffer = new char[str.length + replaceSize]; 93 size_t leftBound = 0; 94 size_t leftBoundInput = 0; 95 size_t rightBound = 0; 96 size_t currArg = 0; 97 for(int i = 0; i < str.length; i++) 98 { 99 if(i + 1 < str.length && str[i] == '%' && str[i+1] == 's') 100 { 101 buffer[leftBound..rightBound] = str[leftBoundInput..i]; 102 i++; 103 leftBoundInput = i+1; 104 string r = replaceWith[currArg++]; 105 buffer[rightBound..rightBound+r.length] = r[]; 106 leftBound = rightBound = rightBound+r.length; 107 if(currArg == replaceWith.length) 108 break; 109 } 110 else 111 rightBound++; 112 } 113 buffer[leftBound..$] = str[leftBoundInput..$]; 114 return cast(string)buffer; 115 }